class Machine

  @@cmds = %w"load store read write add mult sub div jump jzero jgtz halt"


private

  def _load(arg)
    @memory[0] = @memory[arg.to_i]
  end
  
  def _load=(arg)
    @memory[0] = arg.to_i
  end
  
  def _store(arg)
    @memory[arg.to_i] = @memory[0]
  end
  
  def _read(arg)
    @memory[arg.to_i] = @input.readline.strip.to_i
  end
  
  def _write(arg)
    @output << @memory[arg.to_i]
    @output << " "
  end
  
  def _write=(arg)
    @output << arg.to_i
    @output << " "
  end
  
  def _add(arg)
    @memory[0] += @memory[arg.to_i]
  end
  
  def _add=(arg)
    @memory[0] += arg.to_i
  end
  
  def _sub(arg)
    @memory[0] -= @memory[arg.to_i]
  end
  
  def _sub=(arg)
    @memory[0] -= arg.to_i
  end
  
  
  def _mul(arg)
    @memory[0] *= @memory[arg.to_i]
  end
  
  def _nul=(arg)
    @memory[0] *= arg.to_i
  end
  
  
  def _div(arg)
    @memory[0] /= @memory[arg.to_i]
  end
  
  def _div=(arg)
    @memory[0] /= arg.to_i
  end
  
  def _jump(arg)
    @pc = arg.to_i
  end
  
  def _jzero(arg)
    @pc = arg.to_i if @memory[0] == 0
  end
  
  def _jgtz(arg)
    @pc = arg.to_i if @memory[0] > 0
  end
  
  def _halt(arg)
    @done = true
  end
  
public

  def initialize
    @labels = {}
    @memory = []
  end

  def run(program)
    init_labels(program)
    load(program)
    p @memory
    File.open("input.txt") { |input|
      @input = input
      @output = File.open("output.txt", "w")
      @log = File.open("log.txt", "w")
      execute
      @output << "\n"
    }
  end

  private

  def execute
    @pc = 1
    @done = false
    while (@pc < @memory.length && !@done)
      inst = @memory[@pc]
      direct = false # addressing type
      if inst >= @@cmds.length
        direct = true
        inst -= @@cmds.length
      end
      
      name = "_" + @@cmds[inst]
      name += "=" if direct
      
      
      @log.puts "=> #{name} ( #{@memory[@pc + 1]} ) , pc = #{@pc}, memory = [#{@memory.join(", ")}]"
      @log.puts "\n"
      
      @pc += 2
      
      self.send(name, @memory[@pc - 1])
      
    end
  end

  def init_labels(program)
    offset = 1
    program.each_line { |l|
      l.strip!
      next if l.length == 0
      if l =~ /^@(\w+).*$/
        puts "found label \"#{$1}\", assigning address #{offset}"
        @labels[$1] = offset
      end
      offset += 2
    }
  end
  
  def load(program)
    @memory = []
    offset = 1
    program.each_line { |l|
      l.strip!
      next if l.length == 0
      next if l =~ /^@(\w+).*$/
      oc = op_code(l)
      arg = arg_code(l)
      @memory[offset] = oc
      @memory[offset + 1] = arg
      offset += 2
    }
  end
  
  def op_code(line)
    line.strip!
    if line =~ /^(\w+)\s+(.*)$/
      op = $1
      res = @@cmds.index(op)
      if !res
        puts "unknown cmd #{op}"
        return nil
      end
      if $2[0].chr == "="
        res += @@cmds.length
      end
      return res
    end
    if line == "halt"
      return @@cmds.index(line)
    end
    nil
  end
  
  def arg_code(line)
    if line =~ /^(\w+)\s+(.*)$/
      if $2[0].chr == "="
        return $2[1..$2.length].to_i
      end
      if $2[0].chr == "@"
        return @labels[$2[1..$2.length]].to_i
      end
      return $2.to_i
    end
    nil
  end
  
end

program = IO.read(ARGV[0])

m = Machine.new

m.run(program)
